/*
 * VeinsAdapter.cc
 *
 *  Created on: Apr 26, 2014
 *      Author: shazbox
 */

#include <VeinsAdapter.h>

bool VeinsAdapter::isCreated = false;
//std::string VeinsAdapter::adapterClass = "jveins/VeinsAdapter";
const char * VeinsAdapter::adapterClass = "jveins/VeinsAdapter";
VeinsAdapter* VeinsAdapter::instance = NULL;
int VeinsAdapter::curID = 17;

VeinsAdapter* VeinsAdapter::getInstance(){
    if(!isCreated){
        instance = new VeinsAdapter();
        isCreated = true;
        std::cout << "Created singleton instance." << std::endl;
    }

    std::cout << "Returned singleton instance." << std::endl;
    return instance;
}

VeinsAdapter::VeinsAdapter() {
    // TODO Auto-generated constructor stub
    printf("Initializing JVM\n");
    //options[0].optionString = "-Djava.class.path=../../JVeinsJava/bin:.";
    options[0].optionString = "-Djava.class.path=/home/shazbox/Traffic/omnetpp-4.4.1/samples/JVeinsJava/bin:../../JVeinsJava/bin:.";

    memset(&vm_args, 0, sizeof(vm_args));
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    if (status != JNI_ERR)
    {
        printf("Successfully initialized JVM\n");
        jclass clazz = env->FindClass(adapterClass);
        if (clazz == 0) {
            printf("Find Class Failed.\n");
        }else{
            printf("Found class.\n");
        }

        static JNINativeMethod signatures[] =
        {
                {"messageToC", "(ILjava/lang/String;)V", (void *)&messageToC},
                {"scheduleSelfMsg", "(ILjava/lang/String;)V", (void *)&scheduleSelfMsg}
        };

        env->RegisterNatives(clazz,signatures,2);
        /*JNINativeMethod nm;
        nm.name = "messageToC";
        nm.signature = "(ILjava/lang/String;)I";
        nm.fnPtr = (void *)&messageToC;
        env->RegisterNatives(clazz,&nm,1);*/


        jmethodID constructor = env->GetMethodID(clazz, "<init>", "()V");
        if (constructor == 0) {
            printf("Find method Failed.\n");
        }else {
            printf("Found method.\n");
        }

        jAdapter = env->NewGlobalRef(env->NewObject(clazz,constructor));

        if(jAdapter == NULL){
            printf("Creation of Java Adapter failed.\n");
            exit(EXIT_FAILURE);
        }else{
            printf("Creation of Java Adapter succeeded.\n");
        }
    }else{
        printf("Error initializing JVM");
        jthrowable exc;
        exc = env->ExceptionOccurred();
        if (exc) {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
    }
}

VeinsAdapter::~VeinsAdapter() {
    // TODO Auto-generated destructor stub
    isCreated = false;
    env->DeleteGlobalRef(jAdapter);
    jvm->DestroyJavaVM();
}

void VeinsAdapter::sendBeacon(int id, std::string msg){
    jmethodID mid = env->GetMethodID(env->FindClass(adapterClass), "onBeacon", "(ILjava/lang/String;)V");
        if(mid == NULL){
            printf("Could not find sendBeacon method.\n");
        }else{
            jstring jmsg = env->NewStringUTF(msg.c_str());
            env->CallVoidMethod(jAdapter,mid, (jint)id, jmsg);
        }
}

void VeinsAdapter::sendData(int id, std::string msg){
    jmethodID mid = env->GetMethodID(env->FindClass(adapterClass), "onData", "(ILjava/lang/String;)V");
    if(mid == NULL){
        printf("Could not find sendData method.\n");
    }else{
        jstring jmsg = env->NewStringUTF(msg.c_str());
        env->CallVoidMethod(jAdapter,mid, (jint)id, jmsg);
    }
}

void VeinsAdapter::sendSelfMsg(int id, short kind){
    jmethodID mid = env->GetMethodID(env->FindClass(adapterClass), "onSelfMsg", "(IS)V");
    if(mid == NULL){
        printf("Could not find sendSelfMsg method.\n");
    }else{
        printf("Forwarding message to java adapter (id=%d)\n", id);
        //jstring jmsg = env->NewStringUTF(msg.c_str());
        env->CallVoidMethod(jAdapter,mid, (jint)id, (jshort)kind);
    }
}

int VeinsAdapter::registerEntity(std::string clazz, JavaVehApp * object){
    jmethodID mid = env->GetMethodID(env->FindClass(adapterClass), "registerEntity", "(Ljava/lang/String;I)V");
    if(mid == NULL){
        printf("Could not find register method.\n");
    }else{
        curID++;
        int vehID = curID;
        jstring jclazz = env->NewStringUTF(clazz.c_str());
        env->CallVoidMethod(jAdapter,mid, jclazz, (jint)vehID);
        map[vehID] = object;
        return vehID;
    }
    return -1;
}

/*JNIEXPORT jint JNICALL Java_jveins_VeinsAdapter_messageToC (JNIEnv *, jobject, jint id, jstring msg){
    printf("Receiving response message from java.\n");
    //const char * message = environment->GetStringUTFChars(msg, 0);
    //printf("Message contents: %s\n", message);
    //VeinsAdapter::getInstance()->map[(int)id]->receiveMessage(message);
    //environment->ReleaseStringUTFChars(msg, message);
    return (jint)72;
}*/

void JNICALL VeinsAdapter::messageToC (JNIEnv * environment, jobject obj, jint id, jstring msg){
    printf("Receiving response message from java.\n");
    const char * message = environment->GetStringUTFChars(msg, 0);
    printf("Message contents: %s\n", message);
    VeinsAdapter::getInstance()->map[(int)id]->receiveMessage(message);
    environment->ReleaseStringUTFChars(msg, message);
    return;
}

void JNICALL VeinsAdapter::scheduleSelfMsg (JNIEnv * environment, jobject obj, jint id, jstring msg){
    printf("Received schedules self message from java.\n");
    const char * message = environment->GetStringUTFChars(msg, 0);
    printf("Message contents: %s\n", message);
    printf("Sending to vehicle id#%d\n", (int)id);
    VeinsAdapter::getInstance()->map[(int)id]->scheduleSelfMsg(message);
    environment->ReleaseStringUTFChars(msg, message);
    return;
}


